import fire
from pathlib import Path
import pandas as pd
from random import randrange
import asyncio
from typing import Tuple, Union, Text
import sys
import socket
import time
import logging

"""
这个程序用于使用QPMonitor中的数据，以UDP的方式发送出来，用于测试新系统的处理能力和可靠性。

实现采用了asyncio的UDP发送。
fastmode=0时，完全按照原有数据模拟
fastmode=1时，按照每0.5s一个数据包进行模拟

软件包：

"""
## 日志写入的设置
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M',
                    filename=r'udp_repeat.log',
                    filemode='w+')

console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
# set a format which is simpler for console use
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')


logger = logging.getLogger('')
logger.addHandler(console)
logger.debug('test')


def udp_repeat_init(playback_dir=r"F:\workspace\航测软件\PlaybackData\2018-04-05_Flight_01"):
    """
    输入QPMonitor文件夹，自动利用asycio udp广播
    """
    playback_dir = Path(playback_dir)
    simlist_path = playback_dir/'_SIMLIST.TXT'
    simlist = pd.read_csv(simlist_path, names='filename,group_name,port'.split(
        ','), skip_blank_lines=True)
    logger.debug(simlist)
    all_data = {}

    for idx, (filename, group_name, port) in simlist.iterrows():
        file_path = playback_dir/filename
        if file_path.exists():
            logger.debug(f'{idx:2d}:, {file_path}, {group_name}, {port},{file_path.stat().st_size//1024/1024:.3f} MB')
            # @note: ISO-8859-1 is Western Europe charset encoding, and was the default character in HTML 4.01.
            timestamp_str_list = []
            msg_list = []
            for line in open(file_path, 'r', encoding='ISO-8859-1').readlines():
                # should include timestamp like '2018-04-05 18:45:00,'
                assert len(line.rstrip()) >= 19
                timestamp_str = line[0:19]
                # ignore blank space or comma
                msg = line[20:].strip()
                timestamp_str_list.append(timestamp_str)
                msg_list.append(msg)

            _ts_idx = pd.to_datetime(timestamp_str_list)
            all_data[filename] = pd.DataFrame(
                msg_list, index=_ts_idx, columns=['msg'])
            logger.debug(all_data[filename].head())
            logger.debug(f'shape={all_data[filename].shape}')
        else:
            logger.warning(f'{file_path} do not exist!')
    return all_data


class BroadcastProtocol(asyncio.DatagramProtocol):
    """
    广播出去
    """
    Address = Tuple[str, int]

    def __init__(self, target: Address, *, loop: asyncio.AbstractEventLoop = None, data=None, fastmode=0):
        """
        Address like: ('192.168.1.255',8000) # 组播到192.168.1.xxx子网
        """
        self.target = target
        self.loop = asyncio.get_event_loop() if loop is None else loop
        self.data = udp_repeat_init() if data is None else data
        self.fastmode=fastmode

    def connection_made(self, transport: asyncio.transports.DatagramTransport):
        logger.debug('started')
        self.transport = transport
        sock = transport.get_extra_info("socket")  # type: socket.socket
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        for name in self.data.keys():   
            # Schedule the callback at the next iteration of the event loop
            self.loop.call_soon_threadsafe(self.broadcast, name)

    def broadcast(self,name, iloc_idx=0):
        """
        send udp packet to self.target repeatedly
        """
        df = self.data[name]
        msg = ','.join([str(e) for e in df.iloc[iloc_idx]])+'\n'
        b_msg = msg.encode()
        # asyncio sendto() is non-blocking
        self.transport.sendto(b_msg, self.target)

        next_iloc_idx = iloc_idx+1
        if next_iloc_idx < df.shape[0]:
            delay_time_in_secs = (df.index[next_iloc_idx] - df.index[iloc_idx]).total_seconds()
            if self.fastmode==1:
                delay_time_in_secs = 0.5 
             # Schedule the callback with delay
            self.loop.call_later(delay_time_in_secs, self.broadcast, name, next_iloc_idx)
        else:
            return

def main():
    loop = asyncio.get_event_loop()
    coro = loop.create_datagram_endpoint(
        lambda: BroadcastProtocol(('192.168.1.255', 9000), loop=loop,fastmode=1), local_addr=('0.0.0.0', 9000))
    loop.run_until_complete(coro)
    loop.run_forever()


if __name__ == "__main__":
    sys.exit(main())